package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
)

func init() {
	flag.Usage = func() {
		println(`这个程序需要配合我编写的安卓TTS（语音合成）服务器tts_server使用，两者必须在同一网络中运行（使用手机或者安卓模拟器运行tts_server）。
命令格式为：
./text2audio [-addr ServerIP] [-i filename] [-d dirname] [Output Name]
或者：
echo '语句' | ./text2audio [-addr ServerIP] [Output Name]
或者：
cat 文件名.txt | ./text2audio [-addr ServerIP] [Output Name]
参数说明：
	-addr: 服务器地址，IP地址可以从安卓的设置界面查看，（默认值：192.168.240.112，waydroid模拟器通常是这个地址），如果使用xdroid模拟器,通常是192.168.252.11。
		可以用HOME/.text2audio配置默认地址，格式为JSON：{ "Addr":"xxx.xxx.xxx.xxx" }
	Output Name: 输出文件名，如果空着，就不输出文件，只支持将来自标准输入的文本转换为音频。
	-i：文件名，用于播放单个文件，不能输出音频文件，会记录进度。
	-d：目录名，用于播放整个目录，不能输出音频文件，会记录进度。
说明：本程序需要调用ffmpeg合并WAV文件、转换音频格式，所以请先确认安装了ffmpeg
		`)
	}
	getServer()
}

const tmp = ".tts_tmp"

var serverCfg struct {
	Addr string
}

/*
config file path: HOME/.text2audio

format:

	{
		"Addr":"192.168.0.1"
	}
*/
func getServer() string {
	serverCfg.Addr = "192.168.240.112"
	home1, err := os.UserHomeDir()
	if err != nil {
		log.Println(err.Error())
		return serverCfg.Addr
	}
	data, err := ioutil.ReadFile(filepath.Join(home1, ".text2audio"))
	if err != nil {
		log.Println(err.Error())
		return serverCfg.Addr
	}
	err = json.Unmarshal(data, &serverCfg)
	if err != nil {
		log.Println(err.Error())
		return serverCfg.Addr
	}
	return serverCfg.Addr
}

func main() {
	// Run 'backgroundtask' in xdroid : 192.168.252.11
	// Run 'backgroundtask' in waydroid : 192.168.240.112
	var addr = flag.String("addr", "", "tts server address 'IP:port'")
	var input1 = flag.String("i", "", "播放文件")
	var dir1 = flag.String("d", "", "播放文明路")
	flag.Parse()

	if *addr != "" {
		serverCfg.Addr = *addr
	}

	os.MkdirAll(tmp, os.ModePerm)
	defer os.RemoveAll(tmp)

	if len(*input1) > 0 {
		log1 := getLog(*input1)
		var start = 0
		if log1 != nil {
			start = log1.Pos
		}
		err := playFile(*input1, start, serverCfg.Addr)
		if err != nil {
			panic(err)
		}
		return
	}

	if len(*dir1) > 0 {
		err := playDir(*dir1, serverCfg.Addr)
		if err != nil {
			panic(err)
		}
		return
	}

	println("用法：输入内容后先按回车键，然后按 Ctrl-d 结束输入。")
	var text = []byte{}
	for {
		var val [1024]byte
		n, err := os.Stdin.Read(val[:])
		if err != nil || n == 0 {
			break
		}
		text = append(text, val[:n]...)
	}
	text = append(text, '\r', '\n')
	//print(string(text))
	list1 := splitText(string(text))
	os.MkdirAll(tmp, os.ModePerm)
	var names = []string{}
	var lines = []string{}
	for i, s := range list1 {
		println(i, s, len(s))
		name := fmt.Sprintf("a%v.wav", i)
		lines = append(lines, fmt.Sprintf("file '%s'", name))
		names = append(names, name)
		convertToWav(s, filepath.Join(tmp, name), serverCfg.Addr)
	}

	listfp, err := os.Create(filepath.Join(tmp, "list.txt"))
	if err != nil {
		panic(err)
	}
	listfp.Write([]byte(strings.Join(lines, "\n")))
	listfp.Close()
	if flag.Arg(0) == "" {
		println("Output to path: ./tmp/")
		return
	}
	if len(names) > 1 {
		mergeToTarget(flag.Arg(0))
	} else if len(names) == 1 {
		convertWavToTarget(names[0], flag.Arg(0))
	}
}

func convertWavToTarget(wavName, mp3Name string) {
	cmd := exec.Command("ffmpeg", "-i", filepath.Join(tmp, wavName), "-y", mp3Name)
	err := cmd.Run()
	if err != nil {
		panic(err)
	}

	os.RemoveAll(tmp)
}

func mergeToTarget(filename string) {
	cmd := exec.Command("ffmpeg", "-f", "concat", "-i", filepath.Join(tmp, "list.txt"), "-y", filename)
	err := cmd.Run()
	if err != nil {
		panic(err)
	}

	os.RemoveAll(tmp)
}

func convertToWav(text, filename, serverAddr string) error {
	conn, err := net.Dial("tcp", serverAddr+":9800")
	if err != nil {
		return err
	}
	defer conn.Close()

	length := len(text)
	fmt.Printf("%08d\n", length)
	conn.Write([]byte(fmt.Sprintf("%8d", length)))
	conn.Write([]byte(text))

	data := []byte{}
	decoder := json.NewDecoder(conn)

	err = decoder.Decode(&data)
	out, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer out.Close()
	out.Write(data)
	return nil
}

func splitText(text string) []string {
	var r1 = regexp.MustCompile("[，。！？；：”’,.:!?;\"']+")
	res := r1.FindAllStringIndex(text, -1)
	const limit = 120
	var ret1 = []string{}
	var start = 0
	//fmt.Printf("%#v\n", res)
	for _, idx := range res {
		ret1 = append(ret1, strings.TrimSpace(text[start:idx[1]]))
		start = idx[1]
	}
	if start < len(text) {
		ret1 = append(ret1, strings.TrimSpace(text[start:]))
	}
	var ret = []string{}
	start = 0
	for i, _ := range ret1 {
		s := strings.Join(ret1[start:i+1], "")
		if len(s) > limit {
			if i > start {
				ret = append(ret, strings.Join(ret1[start:i], ""))
				start = i
			} else {
				ret = append(ret, s)
				start = i + 1
			}
		}
	}
	if start <= len(ret1) {
		ret = append(ret, strings.Join(ret1[start:len(ret1)], ""))
	}
	return ret
}
